package org.dromara.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.websocket.connection.ConnectionListener;
import org.dromara.websocket.connection.ConnectionManager;
import org.dromara.websocket.connection.ConnectionManagerInboundHandlerAdapter;
import org.dromara.websocket.message.MessageListener;
import org.dromara.websocket.message.TextWebSocketFrameInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

/**
 * Created by       PokerFace
 * Create Date      2023-09-28
 * Email:
 * Version          1.0.0
 * <p>
 * Description:
 */
public class NettyWebsocketServer {

    private static final Logger logger = LoggerFactory.getLogger(NettyWebsocketServer.class);

    /**
     *  server context path
     *
     */
    private String path = "";


    private int port;

    /**
     *  容器是否启动
     *
     */
    private volatile boolean bind = false;


    private boolean tcpNoDelay;


    private boolean keepalive;


    private ConnectionManager connectionManager;


    private ServerBootstrap bootstrap;


    private MessageListener messageListener;


    private String fullchain;

    private String privkey;


    /**
     * {@link Builder#build()}  用builder来构建
     *
     */
    private NettyWebsocketServer() {
        connectionManager = new ConnectionManager();
    }

    private void init() {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, keepalive)
                .option(ChannelOption.TCP_NODELAY, tcpNoDelay)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) {
                        ChannelPipeline pipeline = ch.pipeline();

                        if (StringUtils.isNotEmpty(fullchain) && StringUtils.isNotEmpty(privkey)) {
                            try{
                                // 加载证书
                                SslContextBuilder builder = SslContextBuilder.forServer(
                                    new File(fullchain),
                                    new File(privkey),
                                    null
                                )
                                    .sslProvider(SslProvider.JDK);
                                SslContext sslContext = builder
                                    .build();

                                pipeline.addLast(sslContext.newHandler(ch.alloc()));
                            } catch (Exception e) {
                                logger.error(e.getMessage(), e);
                            }
                        }

                        pipeline.addLast(new ConnectionManagerInboundHandlerAdapter(connectionManager));
                        //websocket协议本身是基于http协议的，所以这边也要使用http解编码器
                        pipeline.addLast(new HttpServerCodec());
                        //以块的方式来写的处理器
                        pipeline.addLast(new ChunkedWriteHandler());
                        //netty是基于分段请求的，HttpObjectAggregator的作用是将请求分段再聚合,参数是聚合字节的最大长度
                        pipeline.addLast(new HttpObjectAggregator(8192));
                        //ws://server:port/context_path
                        //ws://localhost:9999/ws
                        //参数指的是context_path
                        pipeline.addLast(new WebSocketServerProtocolHandler(path));
                        //websocket定义了传递数据的6中frame类型
                        pipeline.addLast(new TextWebSocketFrameInboundHandler(messageListener, connectionManager));

                    }
                });
    }


    /**
     * 开始监听
     *
     */
    protected void bind() {
        bootstrap.bind(port).addListener(future -> {
            if (future.isSuccess()) {
                this.bind = true;
                logger.info("Start NettyServer Success!! listened port [ " + port + " ] context:{}", path);
            } else {
                logger.error("Start NettyServer failed! ...!");
            }
        });
    }


    /**
     *  启动容器。绑定到某个端口
     *
     * @param port
     */
    public void start(int port) {
        this.port = port;
        this.bind = false;
        bind();
    }





    public int getPort() {
        return port;
    }

    public static Builder builder() {
        return new Builder();
    }



    public static class Builder{
        private boolean delay;
        private boolean keepalive;
        private String contextPath;
        private ConnectionListener listener;
        private MessageListener messageListener;
        private String fullchain;
        private String privkey;

        public Builder tcpNoDelay(boolean delay) {
            this.delay = delay;
            return this;
        }


        public Builder socketKeepalive(boolean keepalive) {
            this.keepalive = keepalive;
            return this;
        }


        public Builder contextPath(String path) {
            this.contextPath = path;
            return this;
        }


        public Builder connectionListener(ConnectionListener listener) {
            this.listener = listener;
            return this;
        }


        public Builder messageListener(MessageListener messageListener) {
            this.messageListener = messageListener;
            return this;
        }



        public Builder ssl(String fullchain, String privkey) {
            this.fullchain = fullchain;
            this.privkey = privkey;
            return this;
        }




        public NettyWebsocketServer build() {
            NettyWebsocketServer server = new NettyWebsocketServer();
            server.tcpNoDelay = this.delay;
            server.keepalive = this.keepalive;
            server.path = this.contextPath;
            server.connectionManager.setConnectionListener(this.listener);
            server.messageListener = this.messageListener;
            server.fullchain = this.fullchain;
            server.privkey = this.privkey;
            // 初始化
            server.init();
            return server;
        }

    }
}
